#ifndef __CAMERA_H
#define __CAMERA_H

#include "Vector3.h"
#include "Matrix4.h"
#include <iostream>
#include <cassert>

using namespace std;

class Camera {
 public:
	Camera(Vector3 pos, Vector3 lookAt, Vector3 up)
		: inverseRotation(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
		rotation(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
		inverseTranslation(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
		translation(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
		_camPos(pos),
		  _lookAt(lookAt),
		_upVector(up),
		_camMatrix(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
		  {
			  recalculateCamMatrix();
		  }
	
	Vector3 const& camPos() const { return _camPos; }
	void camPos(Vector3 const& vector) { _camPos=vector; recalculateCamMatrix(); }

	Vector3 const& lookAt() const { return _lookAt; }
	void lookAt(Vector3 const& vector) { _lookAt=vector; recalculateCamMatrix(); }

	Vector3 const& upVector() const { return _upVector; }
	void upVector(Vector3 const& vector) { _upVector=vector; recalculateCamMatrix(); }
	
	Matrix4 get() {
		return _camMatrix;
	}

	Matrix4 getInverse() {
		return _camMatrix.getInverse();
	}

	void loadIntoOpenGL() {
		glLoadMatrixd(getInverse().getGLMatrix()); 
	}

	Matrix4 inverseRotation;
	Matrix4 rotation;
	Matrix4 inverseTranslation;
	Matrix4 translation;
 private:
	Vector3 _camPos;
	Vector3 _lookAt;
	Vector3 _upVector;
	Matrix4 _camMatrix;

	void recalculateCamMatrix() {
		_camMatrix.identity();

		Vector3 diff = _camPos-_lookAt;

		double constant = (1.0/(_camPos-_lookAt).length());
		Vector3 Z = (diff)*constant;

		Vector3 X = Vector3::cross(_upVector,Z)*(1.0/(Vector3::cross(upVector(), Z).length()));

		Vector3 Y = Vector3::cross(Z, X);

		_camMatrix.m[0][0] = X[0];
		_camMatrix.m[1][0] = X[1];
		_camMatrix.m[2][0] = X[2];
		
		_camMatrix.m[0][1] = Y[0];
		_camMatrix.m[1][1] = Y[1];
		_camMatrix.m[2][1] = Y[2];

		_camMatrix.m[0][2] = Z[0];
		_camMatrix.m[1][2] = Z[1];
		_camMatrix.m[2][2] = Z[2];

		_camMatrix.m[0][3] = _camPos.v[0];
		_camMatrix.m[1][3] = _camPos.v[1];
		_camMatrix.m[2][3] = _camPos.v[2];

		_camMatrix.m[3][3] = 1;
	}
};

#endif
